#ifndef GEOD_POINT_H
#define GEOD_POINT_H
#include <ostream>
#include <qdebug.h>
#include "Def.h"
#include "Constants.h"
#include "Algo.h"

class GeodPoint 
{
public:
	GeodPoint();

	static GeodPoint fromRad(double lon,double lat);
	static GeodPoint fromDeg(double lon,double lat);
	static GeodPoint fromRadFt(double lon,double lat,double elevation);
	static GeodPoint fromDegFt(double lon,double lat,double elevation);
	static GeodPoint fromRadM(double lon,double lat,double elevation);
	static GeodPoint fromDegM(double lon,double lat,double elevation);
	static GeodPoint fromGeodM(const GeodPoint& geod,double elevation);
	static GeodPoint fromGeodFt(const GeodPoint& geod,double elevation);

	double getLongitudeRad(void) const;
	void setLongitudeRad(double lon);

	double getLongitudeDeg(void) const;
	void setLongitudeDeg(double lon);

	double getLatitudeRad(void) const;
	void setLatitudeRad(double lat);

	double getLatitudeDeg(void) const;
	void setLatitudeDeg(double lat);

	double getElevationM(void) const;
	void setElevationM(double elevation);

	double getElevationFt(void) const;
	void setElevationFt(double elevation);

	bool operator == (const GeodPoint & other) const;

	bool isValid() const;
private:
	GeodPoint(double lon,double lat,double elevation);

	double lon_;
	double lat_;
	double elevation_;
};

inline GeodPoint::GeodPoint(void):lon_(0),lat_(0),elevation_(0)
{
}

inline GeodPoint::GeodPoint(double lon,double lat,double elevation) :
lon_(lon),
lat_(lat),
elevation_(elevation)
{
}

inline GeodPoint GeodPoint::fromRad(double lon,double lat)
{
#ifdef GEOD_NATIVE_DEGREE
	return GeodPoint(lon*SGD_RADIANS_TO_DEGREES,lat*SGD_RADIANS_TO_DEGREES,0);
#else
	return GeodPoint(lon,lat,0);
#endif
}

inline GeodPoint GeodPoint::fromDeg(double lon,double lat)
{
#ifdef GEOD_NATIVE_DEGREE
	return GeodPoint(lon,lat,0);
#else
	return GeodPoint(lon*DEGREES_TO_RADIANS,lat*DEGREES_TO_RADIANS,0);
#endif
}

inline GeodPoint GeodPoint::fromRadFt(double lon,double lat,double elevation)
{
#ifdef GEOD_NATIVE_DEGREE
	return GeodPoint(lon*SGD_RADIANS_TO_DEGREES,lat*SGD_RADIANS_TO_DEGREES,
		elevation*SG_FEET_TO_METER);
#else
	return GeodPoint(lon,lat,elevation*SDK_FEET_TO_METER);
#endif
}

inline GeodPoint GeodPoint::fromDegFt(double lon,double lat,double elevation)
{
#ifdef GEOD_NATIVE_DEGREE
	return GeodPoint(lon,lat,elevation*SG_FEET_TO_METER);
#else
	return GeodPoint(lon*DEGREES_TO_RADIANS,lat*DEGREES_TO_RADIANS,elevation*SDK_FEET_TO_METER);
#endif
}

inline GeodPoint GeodPoint::fromRadM(double lon,double lat,double elevation)
{
#ifdef SG_GEOD_NATIVE_DEGREE
	return GeodPoint(lon*SGD_RADIANS_TO_DEGREES,lat*SGD_RADIANS_TO_DEGREES,
		elevation);
#else
	return GeodPoint(lon,lat,elevation);
#endif
}

inline GeodPoint GeodPoint::fromDegM(double lon,double lat,double elevation)
{
#ifdef GEOD_NATIVE_DEGREE
	return GeodPoint(lon,lat,elevation);
#else
	return GeodPoint(lon*DEGREES_TO_RADIANS,lat*DEGREES_TO_RADIANS,elevation);
#endif
}

inline GeodPoint GeodPoint::fromGeodM(const GeodPoint& geod,double elevation)
{
	return GeodPoint(geod.lon_,geod.lat_,elevation);
}

inline GeodPoint GeodPoint::fromGeodFt(const GeodPoint& geod,double elevation)
{
	return GeodPoint(geod.lon_,geod.lat_,elevation*SDK_FEET_TO_METER);
}
 
inline double GeodPoint::getLongitudeRad()const
{
#ifdef GEOD_NATIVE_DEGREE
	return lon_*DEGREES_TO_RADIANS;
#else
	return lon_;
#endif
}

inline void GeodPoint::setLongitudeRad(double lon)
{
#ifdef GEOD_NATIVE_DEGREE
	lon_ = lon*RADIANS_TO_DEGREES;
#else
	lon_ = lon;
#endif
}

inline double GeodPoint::getLongitudeDeg()const
{
#ifdef GEOD_NATIVE_DEGREE
	return lon_;
#else
	return lon_*RADIANS_TO_DEGREES;
#endif
}

inline void GeodPoint::setLongitudeDeg(double lon)
{
#ifdef GEOD_NATIVE_DEGREE
	lon_ = lon;
#else
	lon_ = lon*DEGREES_TO_RADIANS;
#endif
}

inline double GeodPoint::getLatitudeRad(void) const
{
#ifdef GEOD_NATIVE_DEGREE
	return lat_*DEGREES_TO_RADIANS;
#else
	return lat_;
#endif
}

inline void GeodPoint::setLatitudeRad(double lat)
{
#ifdef DEOD_NATIVE_DEGREE
	lat_ = lat*RADIANS_TO_DEGREES;
#else
	lat_ = lat;
#endif
}

inline double GeodPoint::getLatitudeDeg(void) const
{
#ifdef GEOD_NATIVE_DEGREE
	return lat_;
#else
	return lat_*RADIANS_TO_DEGREES;
#endif
}

inline void GeodPoint::setLatitudeDeg(double lat)
{
#ifdef GEOD_NATIVE_DEGREE
	lat_ = lat;
#else
	lat_ = lat*DEGREES_TO_RADIANS;
#endif
}

inline double GeodPoint::getElevationM()const
{
	return elevation_;
}

inline void GeodPoint::setElevationM(double elevation)
{
	elevation_ = elevation;
}

inline double GeodPoint::getElevationFt()const
{
	return elevation_*SDK_METER_TO_FEET;
}

inline void GeodPoint::setElevationFt(double elevation)
{
	elevation_ = elevation*SDK_FEET_TO_METER;
}

inline bool GeodPoint::operator == (const GeodPoint & other) const
{
	return lon_ == other.lon_ &&
		lat_ == other.lat_ &&
		elevation_ == other.elevation_;
}

inline bool GeodPoint::isValid() const
{
	if(Algo::isNaN(lon_))
		return false;
	if(Algo::isNaN(lat_))
		return false;
#ifdef GEOD_NATIVE_DEGREE
	return (lon_ >= -180.0) && (lon_ <= 180.0) && (lat_ >= -90.0) && (lat_ <= 90.0);
#else
	return (lon_ >= -PI) && (lon_ <= PI) && (lat_ >= -PI_2) && (lat_ <= PI_2);
#endif
}

inline void printGeoPoint(const GeodPoint& point)
{
	qDebug() << "lon = " << point.getLongitudeDeg()
		<< "deg, lat = " << point.getLatitudeDeg()
		<< "deg, elev = " << point.getElevationM()
		<< "m";
}

#endif